import CodeSandboxEmbed from './jsx/utils/CodeSandboxEmbed.js';
import { AddRemoveObstacles, StopReleaseDuck } from './jsx/allLiveEditors';

# Adding Interactivity

In this chapter, you'll learn how to:

- Set up the objects in the scene for mouse interaction
- Interact with objects through event handlers
- Retrieve object and event information from the event handlers

## Adding and Removing Obstacles along the Duck's Path

In previous chapters, you've learned how to interact with Worldview through mouse, keyboard, and programmatic controls, all of which affect the camera. Worldview and its [Command components](#/docs/api/command) also support other [mouse events](#/docs/api/mouse-events), such as `onClick`, `onDoubleClick`, `onMouseDown`, `onMouseMove`, and `onMouseUp`.

In this step, let's bring some interactivity by adding and removing obstacles (big red cubes) along the duck's path.

First, let's add an `id` field to each sphere so that we can identify which one was clicked. The `id` value must be unique positive integers.

```js
const sphereMarker = {
  id: 101,
  pose: {
    orientation: { x: 0, y: 0, z: 0, w: 1 },
    position: { x: 0, y: 0, z: 0 },
  },
  scale: { x: 3, y: 3, z: 3 }, // scale up so it's easy to click
  colors: [],
  points: [],
};
```

Let's also enable manual camera movement, so we can see the duck better.

```js
// use `defaultCameraState` to turn Worldview to uncontrolled component
<Worldview defaultCameraState={{ distance: 160 }}>
```

Next, let's add a `clickedObjectIds` state to keep track of the clicked spheres. We'll also use this `clickedObjectIds` state to generate cube obstacles.

```js
const [clickedObjectIds, setClickedObjectIds] = useState([]);

const obstacleMarkers = Array.from(clickedObjectIds).map((clickedObjectId, index) => {
  const pointIdx = clickedObjectId - sphereMarker.id;
  const position = sphereMarker.points[pointIdx];
  return {
    // Since the `sphereMarker` has used up the id range: 101 ~ 101 + 499 (inclusive, each id represent one sphere object),
    // to make the obstacleMarkers' ids unique, we'll use the range: 600 (sphereMarker.id + step) + index.
    // Learn about id mapping at https://cruise-automation.github.io/webviz/worldview/#/docs/api/mouse-events
    id: sphereMarker.id + steps + index,
    // remember the original clickedObjectId so when the obstacle is clicked, we can
    // remove the obstacle quickly by updating clickedObjectIds
    clickedObjectId,
    pose: {
      orientation: { x: 0, y: 0, z: 0, w: 1 },
      position: position,
    },
    color: { r: 1, g: 0, b: 0, a: 1 }, // red
    scale: { x: 6, y: 6, z: 6 }, // scale up a little so it's bigger than the spheres
  };
});
```

Let's use the clicked sphere id to update the `clickedObjectIds` state, which will be consumed by the cube obstacles. Clicking each obstacle should remove it from the scene.

```js
<Spheres
  onClick={(ev, { objects }) => {
    // click on the object to add an obstacle
    setClickedObjectIds([...clickedObjectIds, objects[0].object.id]);
  }}
>
  {[sphereMarker]}
</Spheres>
<Cubes
  onClick={(ev, { objects }) => {
    // When we generate the `obstacleMarkers`, we did add an extra field `clickedObjectId`
    // to the marker so we can use it to remove obstacle id from the `clickedObjectIds`
    const newClickedObjectIds = clickedObjectIds.filter(
      id => id !== objects[0].object.clickedObjectId
    );
    setClickedObjectIds(newClickedObjectIds);
  }}
>
  {obstacleMarkers}
</Cubes>
```

Now we can interact with the scene:

- Whenever we click on a sphere, a red cube will appear in its place.
- Clicking a cube will make it disappear.

<AddRemoveObstacles />

## Stopping and Releasing the Duck

In this step, let's stop the duck from moving forward when it hits one of the obstacles, and clicking on the obstacle will release the duck.

We'll add a `shouldStopDuck` state which will signal the duck when to stop and when to move forward.

```js
// keep track of whether we need to stop the duck from moving
const [shouldStopDuck, setShouldStopDuck] = useState(false);
// use count to set the duck's current position
const [count, setCount] = useState(0);

useAnimationFrame(
  () => {
    const newcount = (count + 1) % steps;
    setCount(newcount);
  },
  shouldStopDuck, // disable the animation when the duck is not moving
  []
);
// ... other existing code
useEffect(
  () => {
    const duckPosId = count + sphereMarker.id;
    // stop the duck if it's moving and it has reached a position occupied by an obstacle
    if (!shouldStopDuck && clickedObjectIds.includes(duckPosId)) {
      setShouldStopDuck(true);
    }
  },
  [count, shouldStopDuck, sphereMarker.id, clickedObjectIds]
);

<Cubes
  onClick={(ev, { objects }) => {
    // ... other existing code
    setShouldStopDuck(false); // release the duck when any cube is clicked
  }}>
  {obstacleMarkers}
</Cubes>;
```

Putting it all together, we now have a moving duck that gets stuck on every cube obstacle in its path. Add an obstacle by clicking any sphere, and clicking the blocking cube that the duck is stuck on will remove the cube itself, allowing the duck to happily continue forward.

<StopReleaseDuck />

## Next Steps

Congratulations on finishing this tutorial and building your first app using Worldview! To learn more:

- Read the [API docs](#/docs/api) to learn common APIs for Worldview, the camera, mouse interaction, type support, etc.
- Read about supported WebGL [Commands](#/docs/commands) – `Cubes`, `Spheres`, `Triangles`, `Lines`, etc.
- Learn more about [regl](http://regl.party/)
- Open a  [pull request](https://github.com/cruise-automation/webviz/pulls) or file an [issues](https://github.com/cruise-automation/webviz/issues)
